#include <stdio.h>
#include <stdlib.h>
#include "lex.h"
#include "lock.h"
#include <string.h>

typedef enum 
{
  OUTSIDE_TAG = 0,
  INSIDE_TAG = 1,
  WITHIN_TAG = 2,
  COMPLETION_TAG = 3
} xreflexer_state_t;


typedef  struct
{
  xreflexer_state_t next_state;
  xreflexer_state_t state;
  xreflexer_state_t prev_state;
} xreflexer_t;

const xreflexer_t DEFAULT_XREFLEXER  =
{
  OUTSIDE_TAG,
  OUTSIDE_TAG,
  OUTSIDE_TAG
};

#if 0
char *statename( int next_state )
{
  switch(next_state)
  {
    case WITHIN_TAG:
      return "WITHIN_TAG";
    case OUTSIDE_TAG:
      return "OUTSIDE_TAG";
    case INSIDE_TAG:
      return "INSIDE_TAG";
  }
  return "UNDEFINED";
}
#endif


void lex_xref(  FILE* stream_in, FILE* stream_out, 
    FILE* stream_lock, tstack_t *ts,
		void (*parse_xref)(tstack_t*,FILE*,long))
{

  char c;
  str_t tok = DEFAULT_STR;
  xreflexer_t xl = DEFAULT_XREFLEXER;

  str_create( &tok );
  tstack_create( ts );

  xl.state = OUTSIDE_TAG;
  while(  (c=fgetc(stream_in)) != EOF )
  {
		//fprintf( stream_out, "%c", c );
		//fflush(stream_out);

    //update the next_state
    switch(c)
    {
      case '<':
        if ( xl.state != COMPLETION_TAG )
        {
          xl.next_state = WITHIN_TAG;
        }
        else
        {
          //need to be careful within completion
          //tag this is an ugly hack to check for
          //'/' after a < to make sure its the end
          //of a tag not a template
          char t = c;
          c = fgetc(stream_in);
          if ( c=='/' )
          {
           // printf( "here\n");
           // char a[10];
           // for ( int i = 0; i < 10; i++ )
           // {
           //   a[i] = fgetc(stream_in);
           //   printf( "%c", a[i] );
           // }
           // printf( "\n" );
           // for ( int i = 0; i < 10; i++ )
           // {
           //   a[i] = ungetc(a[i],stream_in);
           // }
            xl.next_state = WITHIN_TAG;
          }
          else
          {
            xl.next_state = COMPLETION_TAG;
          }
          ungetc(c,stream_in);
          c = t;
        }
        break;
      case '>':
        if ( xl.state == WITHIN_TAG )
          xl.next_state = INSIDE_TAG;
        else if ( xl.state == COMPLETION_TAG )
          xl.state = COMPLETION_TAG;
        else
          xl.next_state = OUTSIDE_TAG;
        break;
    }
    if ( xl.state != xl.next_state )
    {
      //change of state has occurred
      //do appropriate processing
      if ( xl.state == INSIDE_TAG || xl.state == COMPLETION_TAG )
      {
        tag_t *top;
        tstack_top(ts,&top);
        if ( top )
        {
          make_tok(&top->tok,&tok);
        }
      }
      if ( xl.state == WITHIN_TAG )
      {
        tag_t tag = DEFAULT_TAG;
        make_tag(&tok,&tag);
        tstack_push(ts,&tag);

        if ( ( !strcmp(tag.name,"all-completions") ||
            !strcmp(tag.name,"single-completion") ) && !tag.end )
        {
          xl.next_state = COMPLETION_TAG;
        }
				if ( !strcmp(tag.name,"sync")  )
				{
          long id = 0;
          if ( stream_lock )
            lock_read(stream_lock,&id);
					parse_xref(ts,stream_out,id);
					tstack_free_tags(ts);
				}
      }
      str_clear(&tok);
    }
    //if ( /*c != '\n'
    //       && c != '<'
    //      && c != '>'*/ )
    //{
      str_catc(&tok,c);
    //}
    xl.state = xl.next_state;
    xl.prev_state = xl.state;
  }



  str_free( &tok );
  tstack_free( ts );
}
